package org.zn.note.designpattern;

import java.io.File;
import java.io.FilenameFilter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 策略模式（Strategy Pattern）
 * 对于同一件事，定义N个算法，在运行的时候传入算法
 * <p>
 * 命令模式：不同的算法做不同的事
 * 策略模式：不同的算法做相同的事
 * 程序框架上都差不多
 * <p>
 * 策略模式：比如要做“排序”这件事，但是有很多种算法，可以互相替代。比如一个成熟的框架，可以用策略模式(提供一个扩展的口)，然后具体算法可以开发自己实现。
 * <p>
 * 关于“策略模式可以消除过多的if-else”
 * 比如实际的请求主扫、被扫、退货，如果用if-else
 * if(transType = 主扫) nativeServiceImpl.do()
 * if(transType = 被扫) micropayServiceImpl.do()
 * if(transType = 退货) refundServiceImpl.do()
 * 用策略模式后(通过Spring名字找bean)
 * ApplicationContext.getBean(transType + "ServiceImpl").do()
 * 如果把“业务处理”理解为一件事，然后有主扫业务处理、被扫业务处理、退货业务处理这三种算法，这就是策略模式了
 * 但如果把主扫、被扫、退货理解为三件事，就是命令模式了
 * 再举个例子
 * 今天我要出行out.go()
 * if condition=路程近 then outByWalk.go()
 * if condition=路程远 then outByBike.go()
 * if condition=下雨了 then outByCar.go()
 * 我要出行这件事，有三种出行方式(算法)，通过策略模式：
 * Map<condition, out> map = {路程近, outByWalk; 路程远, outByBike; 下雨了, outByCar}
 * map.get(condition).go()
 * 一般来说，能通多if-else来做判断的，都是一件事，不同的条件下有不同的执行方式(算法)，所以还是策略模式更贴切些
 * <p>
 * 综上所述：从工程上来说，实现if-else转策略模式有2种方式：
 * 1. condition本身和实现类名字有关系，可以转换成bean名字，这种用Spring的ApplicationContext.getBean(BeanName)
 * 2. 维护一个Map<condition, impl>对应关系表
 * <p>
 * 实际例子：
 * {@link File#list(FilenameFilter)} 过滤文件。具体通过什么算法过滤文件，可以自己定义并传入。
 * {@link Arrays#sort(Object[], Comparator)} 数组排序。根据什么来判断比较两个元素的大小，可以自己实现，并传入。
 * {@link ThreadPoolExecutor#ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue, RejectedExecutionHandler)}
 * 最后一个参数RejectedExecutionHandler拒绝策略，也是可以自己实现的
 */
public class Pattern23_Strategy {

    public static void main(String[] args) {
        Context context = new Context(new OperationAdd());
        System.out.println("10 + 5 = " + context.executeStrategy(10, 5));

        context = new Context(new OperationSubtract());
        System.out.println("10 - 5 = " + context.executeStrategy(10, 5));
    }

    private static class Context {
        private Strategy strategy;

        Context(Strategy strategy) {
            this.strategy = strategy;
        }

        int executeStrategy(int num1, int num2) {
            return strategy.doOperation(num1, num2);
        }
    }

    private interface Strategy {
        int doOperation(int num1, int num2);
    }

    private static class OperationAdd implements Strategy {
        @Override
        public int doOperation(int num1, int num2) {
            return num1 + num2;
        }
    }

    private static class OperationSubtract implements Strategy {
        @Override
        public int doOperation(int num1, int num2) {
            return num1 - num2;
        }
    }
}
